package cn.sciento.iam.saas.domain.service;

import cn.sciento.core.oauth.CustomUserDetails;
import cn.sciento.core.oauth.DetailsHelper;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.apache.commons.collections4.CollectionUtils;
import cn.sciento.core.util.AsyncTask;
import cn.sciento.core.util.CommonExecutor;
import cn.sciento.iam.domain.entity.Tenant;
import org.springframework.beans.factory.annotation.Value;

public class TenantEventBus {
  private final List<TenantObserver<?>> tenantObservers;
  
  private final ThreadPoolExecutor poolExecutor;
  
  @Value("${stong.tenant-init.enable-async:true}")
  private boolean asyncInitTenant;
  
  public TenantEventBus(List<TenantObserver<?>> tenantObservers) {
    this
      .tenantObservers = (List<TenantObserver<?>>) Optional.ofNullable(tenantObservers).orElse(new ArrayList()).stream().sorted().collect(Collectors.toList());
    int coreSize = CommonExecutor.getCpuProcessors() * 3;
    this
      
      .poolExecutor = new ThreadPoolExecutor(coreSize, coreSize, 10L, TimeUnit.MINUTES, new LinkedBlockingQueue<>(16384), (new ThreadFactoryBuilder()).setNameFormat("TenantEventBus-%d").build(), new ThreadPoolExecutor.CallerRunsPolicy());
    this.poolExecutor.allowCoreThreadTimeOut(true);
    String poolName = "TenantEventBus";
    CommonExecutor.displayThreadPoolStatus(this.poolExecutor, poolName);
    CommonExecutor.hookShutdownThreadPool(this.poolExecutor, poolName);
  }
  
  public void notifyTenantCreate(Tenant tenant) {
    if (CollectionUtils.isEmpty(this.tenantObservers)) {
      return;
    }
    if (this.asyncInitTenant) {
      asyncNotifyTenantCreate(tenant);
    } else {
      for (TenantObserver<?> tenantObserver : this.tenantObservers) {
        tenantObserver.tenantCreate(tenant);
      }
    } 
  }
  
  public void asyncNotifyTenantCreate(final Tenant tenant) {
    if (CollectionUtils.isEmpty(this.tenantObservers)) {
      return;
    }
    final CustomUserDetails self = DetailsHelper.getUserDetails();
    String taskName = "TenantInit";
    List<AsyncTask<Object>> tasks = this.tenantObservers.stream().map(item -> new AsyncTask<Object>() {
          @Override
          public String taskName() {
            return item.getClass().getSimpleName();
          }
          
          @Override
          public Object doExecute() {
            if (self != null) {
              DetailsHelper.setCustomUserDetails(self);
            }
            return item.tenantCreate(tenant);
          }
        }).collect(Collectors.toList());
    CommonExecutor.batchExecuteAsync(tasks, this.poolExecutor, taskName);
  }
  
  public void notifyTenantUpdate(Tenant tenant) {
    if (CollectionUtils.isEmpty(this.tenantObservers)) {
      return;
    }
    for (TenantObserver<?> tenantObserver : this.tenantObservers) {
      tenantObserver.tenantUpdate(tenant);
    }
  }
}
